home *** CD-ROM | disk | FTP | other *** search
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Fast Fourier Transform
- *
- * Processing the Input Sequence
- *
- * The functions below handle different cases of the input (real/complex,
- * with/without zero padding), form the array A, and perform the radix 2 FFT
- * algorithm itself, taking advantage of the particular form of input.
- *
- * General radix 2 FFT algorithm is as follows
- * Input: x_re[i], x_im[i] the input sequence
- * Output: Aj = SUM[ Xk * W^(k*j) ], j,k = 0:N-1,
- * where
- * W = exp(-2pi I/N)
- * Xk = ( k<N/2 ? x_re[k] + I x_im[k] : 0.0 ) with zero padding
- * Xk = x_re[k] + I x_im[k], without zero padding
- *
- * Algorithm
- * 1. Fill in the complex array A performing the permutation
- * of input data
- * Ai = ( j < N/2 ? x_re[j] + I x_im[j] : 0.0 ), j = 0..N-1
- * or
- * Ai = ( x_re[j] + I x_im[j] ), j = 0..N-1
- * where
- * index i is the "inverse" of the index j (in the sense that
- * m-bit string representing the value of 'i' is read from right
- * to left the string corresponding to index 'j'; m = log2(N)
- *
- * 2. Transform itself
- * n2_l = 1; // 2^l at l=0
- * for l=0 to m-1 do // m = log2(N)
- * for k=0 by 2* n2_l to N-1 do
- * for j=0 to n2_l-1 do
- * compl w = exp(- I j*pi/2^l );
- * i1 = j + k;
- * i2 = i1 + n2_l;
- * A[i1] = A[i1] + A[i2]*w // Batterfly operation
- * A[i2] = A[i1] - A[i2]*w
- * od
- * od
- * n2_l *= 2 // 2^l at the next l
- * od
- *
- * The first three steps of the algorithm above can be performed with
- * simplified formulas from
- * G.Nussbaumer. Fast Fourier Transform and Convolution
- * Algorithms, M., Radio i sviaz, 1985, p.135 (in Russian)
- * The formulas carry out the 8-point FFT for each set of 8 points
- * A[k+0], A[k+1], ... A[k+7], k=0,8,16..N-8
- *
- * The formulas are given below for references, x0-x7 being the source
- * data, y0-y7 the transformed data. Moreover, one has to keep in mind
- * that the source data x0-x7 have been already permuted (as opposite to
- * the book that assumes the source data to be arranged in the natural
- * order rather than permutted one)
- *
- * t1 = x0 + x1 m0 = t7 + t8 s1 = m3 + m4 y0 = m0
- * t2 = x2 + x3 m1 = t7 - t8 s2 = m3 - m4 y1 = s1 + s3
- * t3 = x4 - x5 m2 = t1 - t2 s3 = m6 + m7 y2 = m2 + m5
- * t4 = x4 + x5 m3 = x0 - x1 s4 = m6 - m7 y3 = s2 - s4
- * t5 = x6 + x7 m4 = cu*(t3 - t6) y4 = m1
- * t6 = x6 - x7 m5 = I * (t5 - t4) y5 = s2 + s4
- * t7 = t1 + t2 m6 = I * (x3 - x2) y6 = m2 - m5
- * t8 = t4 + t5 m7 = -Isu* (t3 + t6) y7 = s1 - s3
- *
- * cu = su = sin( pi/4 )
- *
- ************************************************************************
- */
-
- #include "fft.h"
- #pragma implementation "fft.h"
-
- #include <math.h>
- #include <Complex.h>
- #include <std.h>
-
- /*
- *-----------------------------------------------------------------------
- * Perform the transform itself
- * on the complex array A with inplace. First three steps of the algorithm
- * are assumed to have been already performed.
- *
- */
-
- void FFT::complete_transform()
- {
- register int n2_l = 8; // 2^l at l=3
- register int inc = N/2/n2_l; // Increment for the index in W
-
- for(; n2_l < N; n2_l *= 2, inc /= 2)
- {
- register Complex * ak;
- for(ak=A; ak < A_end; ak += 2*n2_l)
- {
- register Complex * aj1 = ak;
- register Complex * aj2 = aj1 + n2_l;
- register Complex * w = W + inc; // Since j=0 case is handled explicitly
-
- Complex ai1 = *aj1;
- Complex ai2w = *aj2; // Batterfly operation in case of
- *aj1++ += ai2w; // j=0 ==> W=1
- *aj2++ = ai1 - ai2w;
- for(; aj1 < ak+n2_l; w += inc)
- {
- Complex ai1 = *aj1; // w = exp(-I 2pi/N j*inc) =
- Complex ai2w = *w * *aj2; // exp(-I j pi/2^l)
- *aj1++ += ai2w;
- *aj2++ = ai1 - ai2w; // Batterfly operation
- }
- }
- }
- }
-
- /*
- *------------------------------------------------------------------------
- * The most general case of complex input without zero padding
- *
- * The Nussbaumer formulas are applied as they are, no further simplification
- * seems possible.
- */
-
- void FFT::input(
- const Vector& x_re, // [0:N-1] vector - Re part of input
- const Vector& x_im) // [0:N-1] vector - Im part of input
- {
- are_compatible(x_re,x_im);
- if( x_re.q_lwb() != 0 || x_re.q_upb() != N-1 )
- _error("Sorry, vector [%d:%d] cannot be processed.\n"
- "Only [0:%d] vectors are valid",
- x_re.q_lwb(), x_re.q_upb(), N-1);
-
- register int k;
- register Complex * ak;
-
- for(ak=A,k=0; k < N; k +=8)
- {
- const double cu = W[ N/8 ].real(); // cos( pi/4 )
-
- register int i;
- i = index_conversion_table[k]; // Index being "reverse" to k
- Complex x0(x_re(i),x_im(i));
-
- i = index_conversion_table[k+1];
- Complex x1(x_re(i),x_im(i));
-
- i = index_conversion_table[k+2];
- Complex x2(x_re(i),x_im(i));
-
- i = index_conversion_table[k+3];
- Complex x3(x_re(i),x_im(i));
-
- i = index_conversion_table[k+4];
- Complex x4(x_re(i),x_im(i));
-
- i = index_conversion_table[k+5];
- Complex x5(x_re(i),x_im(i));
-
- i = index_conversion_table[k+6];
- Complex x6(x_re(i),x_im(i));
-
- i = index_conversion_table[k+7];
- Complex x7(x_re(i),x_im(i));
-
- Complex t1 = x0 + x1;
- Complex t2 = x2 + x3;
- Complex t3 = x4 - x5;
- Complex t4 = x4 + x5;
- Complex t5 = x6 + x7;
- Complex t6 = x6 - x7;
- Complex t7 = t1 + t2;
- Complex t8 = t4 + t5;
-
- #define m2 t1
- m2 -= t2; // Forget t1 from now on
- #define m3 x0
- m3 -= x1; // Forget x0 from now on
- Complex m7 = t3 + t6; m7 = Complex(cu*m7.imag(),-cu*m7.real());
- #define m4 t3 // Forget t3 from now on
- m4 -= t6;
- m4 *= cu;
- Complex m5 = t5 - t4; m5 = Complex(-m5.imag(),m5.real());
- Complex m6 = x3 - x2; m6 = Complex(-m6.imag(),m6.real());
-
- #define s1 m3
- Complex s2 = m3 - m4; // Forget m3 from now on
- s1 += m4;
- #define s3 m6 // Forget m6 from now on
- Complex s4 = m6 - m7;
- s3 += m7;
-
- *ak++ = t7 + t8; // y0
- *ak++ = s1 + s3; // y1
- *ak++ = m2 + m5; // y2
- *ak++ = s2 - s4; // y3
- *ak++ = t7 - t8; // y4
- *ak++ = s2 + s4; // y5
- *ak++ = m2 - m5; // y6
- *ak++ = s1 - s3; // y7
- }
- assert( ak == A_end );
- complete_transform();
- }
- #undef m2
- #undef m3
- #undef m4
- #undef s1
- #undef s3
-
- /*
- *------------------------------------------------------------------------
- * Real input sequence without zero padding
- * When x[j]-s are all real, some intermediate results are either pure
- * real, or pure imaginaire, which are much cheaper to compute than
- * the complex ones. In Nussbauner's formulas above, t1 through t8,
- * m0 through m4, s1 and s2 are all real, whilest m5 through m7, s3, and
- * s4 are pure imaginaire.
- *
- */
-
- void FFT::input(
- const Vector& x_re) // [0:N-1] vector - Re part of input
- {
- x_re.is_valid();
- if( x_re.q_lwb() != 0 || x_re.q_upb() != N-1 )
- _error("Sorry, vector [%d:%d] cannot be processed.\n"
- "Only [0:%d] vectors are valid",
- x_re.q_lwb(), x_re.q_upb(), N-1);
-
- register int k;
- register Complex * ak;
-
- for(ak=A,k=0; k < N; k +=8)
- {
- const double cu = W[ N/8 ].real(); // cos( pi/4 )
-
- register int i;
- i = index_conversion_table[k]; // Index being "reverse" to k
- double x0 = x_re(i);
-
- i = index_conversion_table[k+1];
- double x1 = x_re(i);
-
- i = index_conversion_table[k+2];
- double x2 = x_re(i);
-
- i = index_conversion_table[k+3];
- double x3 = x_re(i);
-
- i = index_conversion_table[k+4];
- double x4 = x_re(i);
-
- i = index_conversion_table[k+5];
- double x5 = x_re(i);
-
- i = index_conversion_table[k+6];
- double x6 = x_re(i);
-
- i = index_conversion_table[k+7];
- double x7 = x_re(i);
-
- double t1 = x0 + x1;
- double t2 = x2 + x3;
- double t3 = x4 - x5;
- double t4 = x4 + x5;
- double t5 = x6 + x7;
- double t6 = x6 - x7;
- double t7 = t1 + t2;
- double t8 = t4 + t5;
-
- #define m2 t1
- m2 -= t2; // Forget t1 from now on
- #define m3 x0
- m3 -= x1; // Forget x0 from now on
- double m7i = -cu*(t3 + t6);
- #define m4 t3 // Forget t3 from now on
- m4 -= t6;
- m4 *= cu;
- double m5i = t5 - t4;
- double m6i = x3 - x2;
-
- #define s1 m3
- double s2 = m3 - m4; // Forget m3 from now on
- s1 += m4;
- #define s3i m6i // Forget m6 from now on
- double s4i = m6i - m7i;
- s3i += m7i;
-
- *ak++ = t7 + t8; // y0
- *ak++ = Complex(s1,s3i); // y1
- *ak++ = Complex(m2,m5i); // y2
- *ak++ = Complex(s2,-s4i); // y3
- *ak++ = t7 - t8; // y4
- *ak++ = Complex(s2,s4i); // y5
- *ak++ = Complex(m2,-m5i); // y6
- *ak++ = Complex(s1,-s3i); // y7
- }
- assert( ak == A_end );
- complete_transform();
- }
- #undef m2
- #undef m3
- #undef m4
- #undef s1
- #undef s3i
-
- /*
- *------------------------------------------------------------------------
- * Complex input with zero padding
- *
- * Note, if index j > N/2, its "inverse", index i is odd. Since the second
- * half of the input data is zero (and isn't specified), x1, x3, x5, and x7
- * in the Nussbaumer formulas above are zeros, and the formulas can be
- * simplified as follows
- *
- * t1 = x0 s1 = x0 + m4 y0 = t7 + t8
- * t2 = x2 s2 = x0 - m4 y1 = s1 + s3
- * t3 = x4 m2 = x0 - x2 s3 = m6 + m7 y2 = m2 + m5
- * t4 = x4 m3 = x0 s4 = m6 - m7 y3 = s2 - s4
- * t5 = x6 m4 = cu*(x4 - x6) y4 = t7 - t8
- * t6 = x6 m5 = -I * (x4 - x6) y5 = s2 + s4
- * t7 = x0 + x2 m6 = -I * x2 y6 = m2 - m5
- * t8 = x4 + x6 m7 = -Isu* (x4 + x6) y7 = s1 - s3
- *
- * cu = su = sin( pi/4 )
- */
-
- void FFT::input_pad0(
- const Vector& x_re, // [0:N/2-1] vector - Re part of input
- const Vector& x_im) // [0:N/2-1] vector - Im part of input
- {
- are_compatible(x_re,x_im);
- if( x_re.q_lwb() != 0 || x_re.q_upb() != N/2-1 )
- _error("Sorry, vector [%d:%d] cannot be processed.\n"
- "Zero padding is assumed, only [0:%d] vectors are valid",
- x_re.q_lwb(), x_re.q_upb(), N/2-1);
-
- register int k;
- register Complex * ak;
-
- for(ak=A,k=0; k < N; k +=8)
- {
- const double cu = W[ N/8 ].real(); // cos( pi/4 )
-
- register int i;
- i = index_conversion_table[k]; // Index being "reverse" to k
- Complex x0(x_re(i),x_im(i));
-
- i = index_conversion_table[k+2];
- Complex x2(x_re(i),x_im(i));
-
- i = index_conversion_table[k+4];
- Complex x4(x_re(i),x_im(i));
-
- i = index_conversion_table[k+6];
- Complex x6(x_re(i),x_im(i));
-
- Complex t7 = x0 + x2;
- Complex t9 = x4 - x6;
- #define t8 x4 // Forget x4 from now on
- t8 += x6;
-
- Complex m2 = x0 - x2;
- Complex m5(t9.imag(),-t9.real());
- Complex m6(x2.imag(),-x2.real());
- Complex m7(cu*t8.imag(),-cu*t8.real());
- #define m4 t9 // Forget t9 from now on
- m4 *= cu;
-
- #define s1 x0
- Complex s2 = x0 - m4; // Forget x0 from now on
- s1 += m4;
- #define s3 m6 // Forget m6 from now on
- Complex s4 = m6 - m7;
- s3 += m7;
-
- *ak++ = t7 + t8; // y0
- *ak++ = s1 + s3; // y1
- *ak++ = m2 + m5; // y2
- *ak++ = s2 - s4; // y3
- *ak++ = t7 - t8; // y4
- *ak++ = s2 + s4; // y5
- *ak++ = m2 - m5; // y6
- *ak++ = s1 - s3; // y7
- }
- assert( ak == A_end );
- complete_transform();
- }
- #undef t8
- #undef m4
- #undef s1
- #undef s3
-
- /*
- *------------------------------------------------------------------------
- * Real input sequence with zero padding
- * Again, since the input is a real sequence, some intermediate results
- * are also real, that makes things simpler.
- */
-
- void FFT::input_pad0(
- const Vector& x_re) // [0:N/2-1] vector - Re part of input
- {
- x_re.is_valid();
- if( x_re.q_lwb() != 0 || x_re.q_upb() != N/2-1 )
- _error("Sorry, vector [%d:%d] cannot be processed.\n"
- "Zero padding is assumed, only [0:%d] vectors are valid",
- x_re.q_lwb(), x_re.q_upb(), N/2-1);
-
- register int k;
- register Complex * ak;
-
- for(ak=A,k=0; k < N; k +=8)
- {
- const double cu = W[ N/8 ].real(); // cos( pi/4 )
-
- register int i;
- i = index_conversion_table[k]; // Index being "reverse" to k
- double x0 = x_re(i);
-
- i = index_conversion_table[k+2];
- double x2 = x_re(i);
-
- i = index_conversion_table[k+4];
- double x4 = x_re(i);
-
- i = index_conversion_table[k+6];
- double x6 = x_re(i);
-
- double t7 = x0 + x2;
- double t9 = x4 - x6;
- #define t8 x4 // Forget x4 from now on
- t8 += x6;
-
- double m2 = x0 - x2;
- double m5i = -t9;
- double m7i = -cu*t8;
- #define m4 t9 // Forget t9 from now on
- m4 *= cu;
-
- #define s1 x0
- double s2 = x0 - m4; // Forget x0 from now on
- s1 += m4;
- double s3i = -x2 + m7i;
- double s4i = -x2 - m7i;
-
- *ak++ = t7 + t8; // y0
- *ak++ = Complex(s1,s3i); // y1
- *ak++ = Complex(m2,m5i); // y2
- *ak++ = Complex(s2,-s4i); // y3
- *ak++ = t7 - t8; // y4
- *ak++ = Complex(s2,s4i); // y5
- *ak++ = Complex(m2,-m5i); // y6
- *ak++ = Complex(s1,-s3i); // y7
- }
- assert( ak == A_end );
- complete_transform();
- }
- #undef t8
- #undef m4
- #undef s1
-